package com.raindrop.rd.parser.token;

import com.raindrop.rd.parser.CharacterStream;
import com.raindrop.rd.parser.match.CharMatch;
import com.raindrop.rd.parser.match.CharTree;
import com.raindrop.rd.parser.match.TokenTree;

import java.util.ArrayList;
import java.util.List;

/**
 * 分词器，主要用于解析code，把每个字符串解析成一个一个的token
 *
 * @author BillDowney
 * @date 2021/3/22 11:03
 */
public class Tokenizer {

    /**
     * 将源码解析成token
     *
     * @param sourceCode 源码
     */
    public static TokenStream parsing(String sourceCode) {
        CharacterStream stream = new CharacterStream(sourceCode);
        List<Token> tokens = new ArrayList<>();
        TokenTree rootTree = TokenTree.create();
        CharTree<TokenType> tmp = rootTree;
        // 遍历源码，转化为token
        while (stream.hasMore()) {
            char curChar = stream.current();
            // 过滤空格和回车
            if (' ' == curChar || '\t' == curChar || '\r' == curChar || '\n' == curChar) {
                stream.offsetIndex();
                continue;
            }
            // 如果不为空，则证明找到了匹配的文法
            if (tmp.contains(curChar)) {
                tmp = tmp.get(curChar);
                stream.startSpan();
                stream.offsetIndex();
                while (stream.hasMore()) {
                    curChar = stream.current();
                    // 匹配到文法继续匹配
                    if (tmp.contains(curChar)) {
                        tmp = tmp.get(curChar);
                        stream.offsetIndex();
                    } else {
                        // 跳出文法匹配
                        break;
                    }
                }
                // 没有匹配到token证明当前是标识符
                if (tmp.hasData()) {
                    // 匹配到了一个token
                    tokens.add(new Token(tmp.getData(), stream.endSpan()));
                    tmp = rootTree;
                } else {
                    while (stream.hasMore()) {
                        if (!CharMatch.IDENTIFIER.contains((int) curChar)) {
                            // 匹配到了一个标识符
                            tokens.add(new Token(TokenType.IDENTIFIER, stream.endSpan()));
                            break;
                        }
                        curChar = stream.consume();
                    }
                }
                continue;
            }
            // 进入数字匹配
            if ('0' <= curChar && '9' >= curChar) {
                stream.startSpan();
                while (stream.hasMore()) {
                    curChar = stream.current();
                    if ('0' > curChar || '9' < curChar) {
                        break;
                    }
                    stream.offsetIndex();
                }
                double a = 1D;
                // 判断具体类型：int,float,double,long
                switch (curChar) {
                    case 'd':
                    case 'D':
                        stream.offsetIndex();
                        tokens.add(new Token(TokenType.DOUBLE_LITERAL, stream.endSpan()));
                        break;
                    case 'f':
                    case 'F':
                        stream.offsetIndex();
                        tokens.add(new Token(TokenType.FLOAT_LITERAL, stream.endSpan()));
                        break;
                    case 'L':
                        stream.offsetIndex();
                        tokens.add(new Token(TokenType.LONG_LITERAL, stream.endSpan()));
                        break;
                    default:
                        // 默认为int
                        tokens.add(new Token(TokenType.INT_LITERAL, stream.endSpan()));
                        break;
                }
                continue;
            }
            // 进入标识符匹配：大小写字母,下划线,数字(不能以数字开头)
            if (CharMatch.IDENTIFIER.contains((int) curChar)) {
                stream.startSpan();
                while (stream.hasMore()) {
                    curChar = stream.current();
                    if (!CharMatch.IDENTIFIER.contains((int) curChar)) {
                        break;
                    }
                    stream.offsetIndex();
                }
                tokens.add(new Token(TokenType.IDENTIFIER, stream.endSpan()));
                continue;
            }
        }
        return new TokenStream(tokens);
    }

}
